{$X-}   {Turn off stack expansion. This is a Lisa concept, not needed on Mac}
{$U-}   {Turn off the Lisa Libraries. This is required by the WorkShop}
{$R-}   {Turn off range checking}

PROGRAM ScrollControls;

{Jeffery J. Bradford    Macintosh Technical Support  April 1985 }

{This example will help you become familiar with how scroll bars work}
{and allow you manipulate the contents of a window. This particular  }
{example remembers where the drawing origin should be and sets the   }
{origin to that spot before drawing every time. This involves some   }
{book keeping, but we've made it pretty simple and minimized globals.}


USES
    {$U Obj/Memtypes  } MemTypes,
    {$U Obj/QuickDraw } QuickDraw,
    {$U Obj/OSIntf    } OSIntf,
    {$U Obj/ToolIntf  } ToolIntf,
    {$U Obj/PackIntf  } PackIntf;

CONST
{menu stuff}
    AppleMenu = 256;
    FileMenu  = 257;
    EditMenu  = 258;

{window IDs}
    WindResID = 256;

{scroll bar res IDs}
    VScrollBar = 256;      {res ID for bottom/horz scroll bar}
    HScrollBar = 257;      {res ID for side/vert scroll bar}

{control stuff}
    didntFindaControl= 0;     {used in IF statements for clarity}
    didntMove        = 0;     {used in IF statements for clarity}
    VSBarWidth       = 15;    {width of vertical scroll bar}
    HSBarHeight      = 15;    {width of horz. scroll bar}
    Line_Inc         = 5;     {amount of scrolling per button press}
    Page_Inc         = 100;   {amount of scrolling per page press}
    HorzScrollBar    = 1;     {used to distinguish between vert & horz scroll bar}
    VertScrollBar    = 0;     {used to distinguish between vert & horz scroll bar}



TYPE
{this is useful stuff you might need sometime}

    WordStuff = Packed Record
       Case Integer of
         0: (Word: Integer);
         1: (SByte1,SByte0: SignedByte);
         2: (b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0: Boolean)
       End;

    CharStuff = Packed Record
         chr3,chr2,chr1,chr0: char;
       End;

    LMwordPtr = ^Integer;          {pointer to low memory address}
    LMLongPtr = ^LongInt;          {pointer to low memory address - long}



VAR
{global program stuff}
    Finished:      Boolean;    {used to terminate the program}
    ClockCursor:   CursHandle; {handle to the waiting watch cursor}

{Screen stuff}
    DragArea:   Rect;          {holds the area where window can be dragged in}
    GrowArea:   Rect;          {holds the area to which a window's size can change}
    Screen:     Rect;          {holds the screen dimensions}

{Window stuff}
    OneWindow:  WindowPtr;     {pointer to the first window}
    UsableArea: Rect;          {uasable area of the windows content region}

{-----------------------------------------------------------------------------
                     end of global variable definition
-----------------------------------------------------------------------------}

PROCEDURE DrawBullseye;
  {this will draw a bullseye that is 700 by 700 pixels }
  Var TempRect:Rect;
      i,a,b,x:integer;
  Begin
     MoveTo(0,0);    LineTo(700,700);
     MoveTo(0,350);  LineTo(700,350);
     MoveTo(0,700);  LineTo(700,0);
     MoveTo(350,0);  LineTo(350,700);

    a := 0;
    b := 700;
    For i := 0 to 11 do
    begin
      x := 56 - i*5;
      a := a + x;
      b := b - x;
      SetRect(TempRect,a,a,b,b);
      FrameRect(TempRect);
    end;

    FillRect(TempRect,black);
End;

{-----------------------------------------------------------------------------}

PROCEDURE DrawDisplay;
Var TempRect:  Rect;  {used for offseting the Usable display area}
    CurOrigin: Point; {holds the current origin}

Begin
{in order to confine the drawing to the usable content area of the window}
{we will set a clipRect of that area}

  TempRect := UsableArea;
  CurOrigin := Point(GetWRefCon(OneWindow));     {get the current origin}

{set up to draw properly, and change the clip}
  SetOrigin(CurOrigin.h, CurOrigin.v);           {account for scrolling}
  OffsetRect(TempRect,CurOrigin.h, CurOrigin.v); {account for scrolling}
  ClipRect(TempRect);

  DrawBullseye;

  SetOrigin(0,0);                   {reset it back to normal}
  ClipRect(OneWindow^.portRect);   {reset it to the original window}
End;

{-----------------------------------------------------------------------------}

PROCEDURE ScrolltheScreen(cntlHdl:ControlHandle);
Var
   Origin:     Point;     {holds the coordinates of the new & old origin}
{  OldOrigin:  Point;      holds the coordinates of the old/last origin}
{  NewOrigin:  Point;      holds the coordinates of the new origin}
  delta_H:    Integer;
  delta_V:    Integer;
  UpDateRgn:  RgnHandle;  {holds the region to be scrolled}

Begin

(*  Code below is for clarity
{get the current (or old) origin}
  OldOrigin := Point(GetWRefCon(OneWindow));

{get the NEW origin, we can only scroll in one direction at a time so}
  If GetCRefCon(CntlHdl) = VertScrollBar
  Then begin
         NewOrigin.v := GetCtlValue(CntlHdl);     {get the new V-value}
         NewOrigin.h := OldOrigin.h;              {use the old H-value}
       end
  Else begin
         NewOrigin.h := GetCtlValue(CntlHdl);     {get the new H-value}
         NewOrigin.v := OldOrigin.v;              {use the old V-value}
       end;

{now find the difference between the old and new origins}
  delta_H := OldOrigin.h - NewOrigin.h;     {get the horizontal change}
  delta_V := OldOrigin.v - NewOrigin.v;     {get the vertical change}
*)

  Origin := Point(GetWRefCon(OneWindow));
  If GetCRefCon(CntlHdl) = VertScrollBar
  Then begin
         delta_H := 0;                                {didn't change}
         delta_V := Origin.v - GetCtlValue(CntlHdl);  {get the diff}
         Origin.v:= GetCtlValue(CntlHdl);             {set to new position}
       end
  Else begin
         delta_V := 0;                                {didn't change}
         delta_H := Origin.h - GetCtlValue(CntlHdl);  {get the diff}
         Origin.h:= GetCtlValue(CntlHdl);             {set to new position}
       end;


{get a region for use in updating/scrolling the screen}
  UpdateRgn := NewRgn;
  ScrollRect(UsableArea, delta_H, delta_V, updateRgn);

  SetOrigin(Origin.h, Origin.v);  {set to the new origin}

{move the update Rgn to new location}
  OffsetRect(updateRgn^^.rgnBBox, Origin.h, Origin.v);

{clip to it}
  ClipRect(updateRgn^^.rgnBBox);

  DrawBullseye;         {do the drawing}

{reset everything back to normal & save the new origin}
  SetOrigin(0,0);
  ClipRect(OneWindow^.portRect);
  SetWRefCon(OneWindow, LongInt(Origin));

  DisposeRgn(updateRgn);
End;

{-----------------------------------------------------------------------------}

PROCEDURE LineScroll(SBar:ControlHandle; ControlLoc:integer);
{get the buttons current value, set the increment and reset it with new value}

Begin
  If ControlLoc = inUpButton
  then SetCtlValue(SBar, GetCtlValue(SBar) - Line_inc)
  else SetCtlValue(SBar, GetCtlValue(SBar) + Line_inc);

  ScrolltheScreen(SBar);
End;

{-----------------------------------------------------------------------------}

PROCEDURE HandleControl_at(atMouseLoc:point; inThisWindow:WindowPtr);
Var
  CntlHdl:     ControlHandle;  {handle of a control part selected}
  ControlLoc:  Integer;        {position ID of the control part selected}
  dummy:       Integer;        {used to hold integer returned from TrackControl}

Begin
  ControlLoc := FindControl(atMouseLoc, inThisWindow, CntlHdl);

  If ControlLoc <> didntFindaControl then
    Case ControlLoc of

      inUpButton:   dummy:= TrackControl(CntlHdl,atMouseLoc,@LineScroll);

      inDownButton: dummy:= TrackControl(CntlHdl,atMouseLoc,@LineScroll);

      inPageUp:    begin
                     SetCtlValue(CntlHdl, GetCtlValue(CntlHdl) - Page_inc);
                     ScrolltheScreen(CntlHdl);
                   end;

      inPageDown:  begin
                     SetCtlValue(CntlHdl, GetCtlValue(CntlHdl) + Page_inc);
                     ScrolltheScreen(CntlHdl);
                   end;

      inThumb:     If TrackControl(CntlHdl,atMouseLoc,Nil) <> didntMove
                      then ScrolltheScreen(CntlHdl);

    End;{case of control location}
End;{of Handle Window Controls}

{-----------------------------------------------------------------------------}

PROCEDURE ProcessMenu_in(CodeWord:longint);
Var
  Menu_No:    integer;        {menu number that was selected}
  Item_No:    integer;        {item in menu that was selected}
  NameHolder: Str255;         {name holder for desk accessory or font}
  DNA:        integer;        {OpenDA will never return 0, so don't care}

Begin
  If CodeWord <> 0 then {go ahead and process the command}
  begin
    Menu_No := HiWord(CodeWord);   {get the Hi word of...}
    Item_no := LoWord(CodeWord);   {get the Lo word of...}

    Case Menu_No of

     AppleMenu: Begin
                  GetItem(GetMHandle(AppleMenu), Item_No, NameHolder);
                  DNA := OpenDeskAcc(NameHolder);
                End;

      FileMenu: Begin
                  Case Item_No of
                    1: Finished := True;          {quit}
                  End;
                End;

      EditMenu: Begin
                  If Not SystemEdit(Item_no - 1) {if not for a desk accessory}
                  then
                    Case Item_No of
                      1: begin end;               {undo}
                    { 2:                           line divider}
                      3: begin end;               {cut}
                      4: begin end;               {copy}
                      5: begin end;               {paste}
                      6: begin end;               {clear}
                    End;
                End;

    End;{case of Menu_No}

    HiliteMenu(0);               {unhilite after processing menu}
  end; {the If codeword <> 0}
End; {of ProcessMenu_in procedure}


{-------------------------------------------------------------------}
{----- These are procedures called from the main event loop  -------}

PROCEDURE DealwthMouseDowns(Event:EventRecord);
Var WindowPointedTo: WindowPtr;
    MouseLoc:Point;
    WindoLoc:integer;
Begin
  MouseLoc := Event.Where;
  WindoLoc := FindWindow(MouseLoc, WindowPointedTo);
  Case WindoLoc of

     inMenuBar: ProcessMenu_in(MenuSelect(MouseLoc));

     inSysWindow: SystemClick(Event,WindowPointedTo);

     inContent: If WindowPointedTo <> FrontWindow
                then SelectWindow(WindowPointedTo)
                else begin
                       GlobaltoLocal(MouseLoc);
                       If Not PtinRect(MouseLoc,UsableArea)
                       then HandleControl_at(MouseLoc, WindowPointedTo)
                       else InvalRect(UsableArea);  {just for effect}
                     end;

     inGrow:    If WindowPointedTo <> FrontWindow
                then SelectWindow(WindowPointedTo)
                else begin {ReSizeWindow(WindowPointedTo,MouseLoc);} end;

     inDrag   :DragWindow(WindowPointedTo,MouseLoc,DragArea);

     inGoAway :If TrackGoAway(WindowPointedTo,MouseLoc)
               then DisposeWindow(WindowPointedTo); {since W mgr allocated space}

  End{ of case};
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthKeyDowns(Event:EventRecord);
Var CharCode:char;
Begin
   CharCode:= CharStuff(Event.message).Chr0; {get low byte w/no processing}

  If BitAnd(Event.modifier,CmdKey) = CmdKey
   then
     begin  {key board command - probably a menu command}
       ProcessMenu_in(MenuKey(CharCode));
     end
   else
     begin
       {regular keyboard entry}
     end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthActivates(Event: EventRecord);
Var TargetWindow:WindowPtr;
    TargetPeek:  WindowPeek;
    CntrlHdl:     ControlHandle;
Begin
   TargetWindow := WindowPtr(Event.message);
   TargetPeek   := WindowPeek(TargetWindow);
   DrawGrowIcon(TargetWindow);

   If Odd(Event.modifiers)

   then {the window is becoming active}
     begin
{1}    SetPort(TargetWindow);

{2}    {activate the window controls}
       CntrlHdl := ControlHandle(TargetPeek^.ControlList);  {get the first one}
       Repeat
         If CntrlHdl <> Nil then   {in case we get a nil on the first one}
         begin
           ShowControl(CntrlHdl);
           CntrlHdl := CntrlHdl^^.nextControl;
         end;
       Until CntrlHdl = NIL;

{3}   {activate selections, etc.}
     end


   else {the window is being DEactivatived}
     begin
{1}   {DEactivate the window controls}
       CntrlHdl := ControlHandle(TargetPeek^.ControlList);  {get the first one}
       Repeat
         If CntrlHdl <> Nil then   {in case we get a nil on the first one}
         begin
           HideControl(CntrlHdl);
           CntrlHdl := CntrlHdl^^.nextControl;
         end;
       Until CntrlHdl = NIL;

{2}    {deactivate selections, etc}
     end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthUpdates(Event:EventRecord);
Var UpDateWindow,
          TempPort: WindowPtr;
Begin
   UpDateWindow := WindowPtr(Event.message);
   GetPort(TempPort);                {Save the current port}

   SetPort    (UpDateWindow);      {set the port to one in Evt.msg}
   BeginUpDate(UpDateWindow);
     EraseRect(UsableArea);
     DrawDisplay;
     DrawControls(UpDateWindow);
     DrawGrowIcon(UpDateWindow);
   EndUpDate  (UpDateWindow);

   SetPort    (TempPort);             {restore to the previous port}
End;

{-----------------------------------------------------------------------------}

PROCEDURE MainEventLoop;
Var Event:EventRecord;
    ProcessIt: Boolean;
Begin
  Repeat
    SystemTask;             {so you can support Desk Accessories}

    ProcessIt := GetNextEvent(EveryEvent,Event);
    If ProcessIt{is true} then {we'll ProcessIt}
          Case Event.what of

            mouseDown  : DealwthMouseDowns(Event);
            KeyDown    : DealwthKeyDowns  (Event);
            ActivateEvt: DealwthActivates (Event);
            UpDateEvt  : DealwthUpdates   (Event);

          End;{of Case}
  Until Finished; {terminate the program}
End;

{-----------------------------------------------------------------------------}

PROCEDURE InitThings;
Begin
  InitGraf(@thePort);          {create a grafport for the screen}

  MoreMasters;                 {extra pointer blocks at the bottom of the heap}
  MoreMasters;                 {this is 5 X 64 master pointers}
  MoreMasters;
  MoreMasters;
  MoreMasters;

{get the cursors we use and lock them down - no clutter}
  ClockCursor := GetCursor(watchCursor);
  HLock(Handle(ClockCursor));

{show the watch while we wait for inits & setups to finish}
  SetCursor(ClockCursor^^);

{init everything in case the app is the Startup App}
  InitFonts;                     {startup the fonts manager}
  InitWindows;                   {startup the window manager}
  InitMenus;                     {startup the menu manager}
  TEInit;                        {startup the text edit manager}
  InitDialogs(Nil);              {startup the dialog manager}

  Finished := False;             {set program terminator to false}
  FlushEvents(everyEvent,0);     {clear events from previous program}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupLimits;
Begin
  Screen := ScreenBits.Bounds;   {set the size of the screen}
  SetRect(DragArea,Screen.left+4,Screen.top+24,Screen.right-4,Screen.bottom-4);
  SetRect(GrowArea,Screen.left,Screen.top+24,Screen.right,Screen.bottom);
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupMenus;
Var
  MenuTopic: MenuHandle;
Begin
  MenuTopic := GetMenu(AppleMenu);  {get the apple desk accessories menu}
  AddResMenu(MenuTopic,'DRVR');     {adds all names into item list}
  InsertMenu(MenuTopic,0);          {put in list held by menu manager}

  MenuTopic := GetMenu(FileMenu);   {always need this for Quiting}
  InsertMenu(MenuTopic,0);

  MenuTopic := GetMenu(EditMenu);   {always need for editing Desk Accessories}
  InsertMenu(MenuTopic,0);

  DrawMenuBar;           {all done so show the menu bar}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupWindows;
Var vScroll:   ControlHandle;  {used to create control}
    hScroll:   ControlHandle;  {same thing, we could put them in global but the}
                               {the window is global and it will have them}
Begin
  OneWindow :=  GetNewWindow(WindResID, Nil, POINTER(-1));
  SetPort(OneWindow);  {so usableArea is correct}

{now setup the usable window area}
  UsableArea := OneWindow^.portRect;                           {initialize it}
  UsableArea.right :=   UsableArea.right - VSBarWidth;
  UsableArea.bottom :=  UsableArea.bottom - HSBarHeight;

{now set up the scroll bar controls that this window will use}
  vScroll := GetNewControl(VScrollBar,OneWindow);  {get vertical scroll bar}
  hScroll := GetNewControl(HScrollBar,OneWindow);  {get horizontal scroll bar}

{our display is going to be 700 by 700 pixels so set min & max values}
{Note: the window is 290 by 490}
  SetCtlMin(hScroll,0);         {set the minimum value in vertical direction}
  SetCtlMax(hScroll,210);       {set the maximum value in vertical direction}
  SetCtlMin(vScroll,0);         {set the minimum value in horizontal direction}
  SetCtlMax(vScroll,410);       {set the maximum value in horizontal direction}

{the current origin will be stored in the refcon of the window}
(*  NOTE: its already been set in the resource file}
  SetWRefCon(OneWindow, 0);             {initialize it to zero}
*)

{all set so show the window}
  ShowWindow(OneWindow);

{Note that the controls are brought in invisibily, show them}
{when the window becomes active, hide them when its inactive}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetUpThings;
Begin
  SetupWindows;         {do first so its low in heap}
  SetupMenus;
  SetupLimits;

  InitCursor;           {ready to go, so show the Arrow cursor}
End;

{-----------------------------------------------------------------------------}

PROCEDURE CloseThings;
Begin
{close files, if you changed sys resources, UNchange them here be carefull}
{about changing sys things, remember the Switcher could be around}
End;

{-----------------------------------------------------------------------------}

BEGIN
  InitThings;
  SetUpThings;
  MainEventLoop;
  CloseThings;
END.
